(post
 :title "State of data-binding (MVVM) in Kivy and Android"
 :date (make-date* 2016 9 23)
 :tags '("data" "data-binding" "kivy" "python" "android" "sdk" "mvvm")
 (h4 [TLDR: Data-binding support is an essential component in the MVVM
            framework. While android has recently adopted data-binding, Kivy has always long had excellent data-binding support])
 (h3 [MVVM and data-binding])
 (p [(Skip if familiar) Application architectures like MVVM
     (Model, View, View-Model) exist to aid in separating view logic from
     business logic. What makes
     MVVM different, is its focus on binding data between the view and the
     view-model. What this means, is that data in the view and in the
     view-model are tightly coupled, in that any registerable event in the
     view, is immediately recognized by the view-model. Additionally, the
     view-model has hooks into the model, so it can format and send any
     changed model data directly to the view.])
 (h3 [Android and MVVM])
 (p [Android was designed, unlike iOS, to be architecture independent.
     For many small applications, this is preferable, since setting up
     MVVM requires many more files and added complexity.
     However, as an application continues
     to grow, it's easy for Android code can get messy. In particular,
     Activities, which already deal with life-cycles, and broadcast
     listeners, also has to manage view events from the view.
     Interacting with the view in activities
     makes for ugly code too: after typecasting each widget we want to
     interact with
     after finding it by id, even simple things like setting text or
     responding to a click
     event are awfully verbose.])

 (code-block-scheme
  (car
 [public class ExampleActivity extends Activity {
    ...
    private EditText messageField;
    private Button submitBtn;

    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.content_layout_id);

        messageField = (EditText) findViewById(R.id.button_id);
        messageField.setText("Hi there!");

        submitBtn = (Button) findViewById(R.id.button_id);
        submitBtn.setOnClickListener(new View.OnClickListener() {
             public void onClick(View v) {
                 // TODO: send message to server
             }
         });
    }
    ...
}]))

 (p [Worse yet, since we are working directly with activities, nearly every
     test requires the full SDK,
     which implies we are stick with slow
     ,(anchor
       [emulation testing]
       "http://www.vogella.com/tutorials/AndroidTesting/article.html").])
 (p [Clearly there was room for improvement, and a number of packages were since
     developed to address these issues. DI frameworks like 
     ,(anchor [ButterKnife ]
              "https://github.com/JakeWharton/butterknife")
     handle automatic view binding])
 (code-block-scheme
  (car
 [public class ExampleActivity extends Activity {
    ...
    @BindView(R.id.edit_id) EditText messageField;
    @OnClick(R.id.button_id) void sendMessage() {
        // TODO: send message to server
    }
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.content_layout_id);

        ButterKnife.bind(this);
        // fields can now be used...
        messageField.setText("Hi there!");
    }
    ...
}]))
(p [and 
         ,(anchor [RoboBindings]
                  "https://github.com/RoboBinding/RoboBinding")
         allow view logic to be handled in a separate ViewModel class.])
 (p [Since DI is separate software concept, let's instead focus on RoboBindings.
     RoboBindings is a great library because
     it allows for strong separation of concerns.
     Now, all the view logic originally in the Activity is implemented in a
     separate ViewModel class, freeing the Activity to focus on its main
     goals: controlling lifecycles, and listening for app/system-wide broadcasts.
     And unlike before, the viewmodel and view directly share the same data,
     and agree on an API to handle events like button clicks. Overall, this makes
     for much cleaner view logic code. ])
(code-block-xml
 (car
  [Our ExampleView XML:

   <LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:bind="http://robobinding.org/android">
       <EditText
           android:id="@+id/editText"
           android:hint="write a message..."
           bind:text="${inputMessage}"/>
       <Button
           android:text="send"
           bind:onClick="sendMessage"/>
   </LinearLayout>]))
(code-block-scheme
 (car [Our ExampleViewModel Class:

 @PresentationModel
 public class ExampleVM {
     private String inputMessage;
 
     public void setInputMessage(String text) {
         this.inputMessage = text;
     }
 
     public String getInputMessage() {
         return this.inputMessage;
     }
     public void sendMessage() {
         // TODO: Send message to server
     }
 }]))

(p [Now, event listeners are gone, since the
         view chooses how to delegate events to the view-model, and in the view-model
         we don't have to manually update any view widget since any data change
         in the view-model automatically updates the view!])
(p [Yeah, data-binding is pretty damn genius. Just one last point: you may have
    noticed that view-models can be represented as standard
     ,(anchor [POJOs]
              "https://en.wikipedia.org/wiki/Plain_Old_Java_Object").
     This makes fast unit testing trivial!])
 (p [Robobinding is of course a 3rd party library, which is somewhat risky for a
     large app developer that needs reliability. Fortunately, Android
     has, since Marshmallow, provided its own
     ,(anchor [data-binding library]
              "https://developer.android.com/topic/libraries/data-binding/index.html")
     While I still personally use robobinding, Android's library looks solid,
     and follows a similar approach.])
 
 (h3 [Okay so what is/why Kivy?])
 (p [I have a feeling y'all are less likely to have heard of
     ,(anchor [Kivy]
                "https://kivy.org"). Essentially
     it's a well-established cross platform application framework written
     in Python. It was originally designed to run on large multi-touch
     screens, but has since been ported to run on Android and iOS. One of
     it's coolest features is
     ,(anchor [kv-lang]
              "https://kivy.org/docs/guide/lang.html"),
     an alternative to XML seen in Android,
     that makes it really easy to build a widget tree, with access to
     the full power of Python. In addition it has amazing data-binding
     support.])
 (p [Since we are familiar with what data-binding looks like in Android, let's
     get right to some code.])
 (ul
  (li [kivy implementation of above:
      ,(code-block-scheme
        (car [KV code: 

<ExampleWidget>:
    <TextInput>:
        text: root.input_message
    <Button>:
        on_click: root.sendMessage()]))
     ,(code-block-scheme
       (car 
 [Python code:
 class ExampleWidget(BoxLayout):
     input_message = StringProperty()

     def send_message(self, btn_instance):
         send_msg_to_server(input_message)]))])
(li [How to use python directly in kv:
     ,(code-block-scheme
       (car
 [#:import string
#:import random
<GenRandStringButton>:
    on_click: root.input_message =
              ''.join(random.SystemRandom()
                .choice(string.ascii_uppercase + string.digits) for _ in range(N))]))]))
(p [While the above code does not implement MVVM directly (databinding
    is managed by a root widget, not a separate view-model class)
    it is clear how expressive and simple databinding under the kivy
    framework can be! I recommend checking out some of the kivy tutorials
    online for more interesting ways to use kv-lang!
    Shameless plug, but you can also check out
    ,(anchor [goblinoid]
             "https://notabug.org/sapientech/goblinoid")
    for more examples.]))
